/**

This file is part of MaCI/GIMnet.

MaCI/GIMnet is free software: you can redistribute it and/or modify it 
under the terms of the GNU Lesser General Public License as published 
by the Free Software Foundation, either version 3 of the License, or 
(at your option) any later version.

MaCI/GIMnet is distributed in the hope that it will be useful, but WITHOUT 
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public 
License for more details.

You should have received a copy of the GNU Lesser General Public 
License along with GIMnet. (See COPYING.LESSER) If not, see 
<http://www.gnu.org/licenses/>.

**/
/**
 * \file
 * \brief ImageContainer class handling type conversions and stuff - Header.
 * \author Antti Maula <antti.maula@tkk.fi>
 *
 */

#ifndef _MACI_INTERFACES_IMAGE_IMAGECONTAINER_HPP_
#define _MACI_INTERFACES_IMAGE_IMAGECONTAINER_HPP_
#include "ImageTypes.hpp"
#include "ImageConversion.hpp"
#include <string>

namespace MaCI {
  namespace Image {

    /** Global function to retrieve the description of vertain
     * ImageDataType.
     * 
     * @return This function returns the verbose format of the
     * ImageDataType as std::string.
     */
    const std::string &GetImageDataTypeDescription(const EImageDataType aType);


    /**
     * Container for image data.
     *
     * This class is a storage class for storing images in different
     * formats. The CImageData class of the Image interface accepts
     * and provides all image data using this class. This class also
     * implements datatype conversions, so application user shouldn't
     * have to implement conversion routines by hand. All possible
     * conversion combinations are not supported, bu can be if
     * requested. If you need a new conversion, please fill in a 'new
     * feature' report in the project bugtracker.
     *
     * \note To compile this Interface without external dependencies
     * to JPEG, PNG etc.  libraries, define compiler macro:
     * 'DISABLE_MACI_IMAGE_IMAGECONTAINER_EXTERNAL_CONVERSIONS'. This will
     * leave out all external dependencies, but will remove
     * functionality to convert data between formats.
     */
    class CImageContainer {
    public:
      /** CImageContainer default constructor.
       *
       */
      CImageContainer();


      /** Constructor for constructing container from GIMnet data.
       *
       * This may be used by applications too, but has a lot less
       * features than the SetImageData() function.
       * 
       * @param[in] aInfo       Directly use this TImageInfo for 
       *                        image information.
       * @param[in] aData       Pointer to data.
       * @param[in] aDataSize   Amount of data in bytes.
       * @param[in] aDuplicate  If this is set to 'true', the 
       *                        data stored in \p aData pointer 
       *                        will be duplicated inside this
       *                        container. 'false' means to
       *                        use the provided pointer directly
       *                        as datasource (remember, 
       *                        if 'false' is used, the original
       *                        buffer must remain valid during
       *                        the lifetime of this object instance.
       *                        (Or until new assign or Reset)
       */
      CImageContainer(const TImageInfo &aInfo,
                      const unsigned char *aData,
                      const unsigned int aDataSize,
                      const bool aDuplicate);

      
      /** Copy constructor.
       *
       */
      CImageContainer(const CImageContainer &aImgData);


      /**
       * Operator '=' working on non-const object -> Assign.
       */
      CImageContainer &operator=(CImageContainer &aImgData);
      
      
      /**
       * Operator '=' working on const object -> Copy.
       */
      CImageContainer &operator=(const CImageContainer &aImgData);


      /** Destructor.
       */
      ~CImageContainer();



      /** Store image in container (with options)
       *
       * This function assigns a databuffer for the container. If the
       * buffer is set to be duplicated, the data is replicated inside
       * the container and no dependencies to the original buffer
       * exist. If the buffer is NOT duplicated, the given pointer /
       * data must remain valid during the lifetime of this object (or
       * until next Reset())
       * 
       * On basic case, this function only requires the three first
       * parameters; data pointer, size and source image type.
       *
       * This function is also capable of carrying out some basic type
       * conversions. (More may be available in the future if
       * required) If the destination type parameter is set to
       * something else than 'KImageDataUnknown', the container will
       * attempt to convert data from given sourcetype to destination
       * type.
       * \n
       * For a list of currently supported data conversions, see
       * description of function ConvertTo().
       * \n
       * Conversions with identical source/dest types will
       * always succeed.
       *
       *
       * @param[in] aData       Pointer to source data.
       * @param[in] aSize       Number of bytes of source data available.
       * @param[in] aSrcType    Source data type. This must not be of type 
       *                        Unknown. If you really need to send something
       *                        no supported by the ImageContainer, use the
       *                        'KImageDataRaw' instead.
       * @param[in] aWidth      Image width in pixels.
       * @param[in] aHeight     Image height in pixels.
       * @param[in] aDestType   If this is set to 'KImageDataUnknown', the
       *                        image is stored as-is (in the Source format,
       *                        that is). If this is set to something else
       *                        whereas the SrcType is not DestType, an 
       *                        conversion will be attempted. If the
       *                        conversion is not supported the function
       *                        returns error. The converted data is owned
       *                        by the class instance, and no dependencies
       *                        to the original data exist.
       * @param[in] aDuplicateSource If this is set to 'true', the given source
       *                        data is duplicated and stored inside this
       *                        class instance. If this is left 'false',
       *                        the data is referenced directly from the
       *                        given source pointer.
       * @return                'true' when the image was succesfully stored
       *                        using the given parameters. 'false' when
       *                        storing the image failed. (For example;
       *                        requested conversion is not supported)
       */
      bool SetImageData(const unsigned char *aData, const unsigned int aSize,
                        const EImageDataType aSrcType,
                        const unsigned int aWidth, const unsigned int aHeight,
                        const EImageDataType aDestType = KImageDataUnknown,
                        const bool aDuplicateSource = false);

      
      /** Store image in container by loading it from filesystem.
       *
       * This function reads a file and assigns a databuffer accordingly.
       * 
       * On basic case, this function only requires the first
       * parameter; the filename.
       *
       * This function is also capable of carrying out some basic type
       * conversions. (More may be available in the future if
       * required) If the destination type parameter is set to
       * something else than 'KImageDataUnknown', the container will
       * attempt to convert data from given sourcetype to destination
       * type.
       * \n
       * For a list of currently supported data conversions, see
       * description of function ConvertTo().
       * \n
       * Conversions with identical source/dest types will
       * always succeed.
       *
       *
       * @param[in] aFilename   Filename to load data from.
       * @param[in] aSrcType    Source data type. The given filename as interpreted
       *                        as this kind of data. If this is set to 'KImageDataUnknown',
       *                        the function will try to guess the image format based on filename
       *                        extension. Note that this function can only read fully defined
       *                        dataformats which contain image dimensions.
       * @param[in] aDestType   If this is set to 'KImageDataUnknown', the
       *                        image is stored as-is (in the Source format,
       *                        that is). If this is set to something else
       *                        whereas the SrcType is not DestType, an 
       *                        conversion will be attempted. If the
       *                        conversion is not supported the function
       *                        returns error. The converted data is owned
       *                        by the class instance, and no dependencies
       *                        to the original data exist.
       * @return                'true' when the image was succesfully stored
       *                        using the given parameters. 'false' when
       *                        storing the image failed. (For example;
       *                        requested conversion is not supported)
       */
      bool SetImageDataFromFile(const std::string &aFilename,
                                const EImageDataType aSrcType = KImageDataUnknown,
                                const EImageDataType aDestType = KImageDataUnknown);


      /** Convert current contents of container to another
       * imageformat.
       *
       * This function attempts to convert the data currently inside
       * the container to the specified dataformat. After the data is
       * converted to desired format, contents of the container can be
       * retrieved by calling 'GetImageDataPtr()' function. Current
       * format parameters of image can be inspected by calling
       * function 'GetImageDataType()'.
       * \n
       * Currently supported data conversions are:\n
       * RGB -> JPEG, \n
       * JPEG -> RGB, \n
       * YUV420P -> JPEG, \n
       * YUV422 -> JPEG \n
       * Greyscale -> JPEG \n
       *
       * \see GetImageDataPtr
       * \see GetImageDataSize
       * \see GetImageDataType
        * \see GetImageInfoRef
       *
       * @param[in] aDestType   Format to attempt conversion to.
       * @return                'true' when conversion was succesfull,
       *                        'false' when conversion could not be
       *                        done 
       */
      bool ConvertTo(const EImageDataType aDestType);


      /** Reset container to empty state.
       *
       * This function resets the container to initial empty
       * state. All references to external dataelements are discarded,
       * and internally allocated buffers are freed.
       */
      void Reset(void);
      
      
      /** Assign function.
       *
       * This claims the ownership for any buffer contained in the
       * source CImageContainer. This is done by assigning the buffer
       * pointer and invaliding the source element (by marking the
       * source buffer NULL)
       *
       * @param[in] aImgData Reference to source to assign from.
       */
      void Assign(CImageContainer &aImgData);
      

      /** Copy function.
       *
       * This copies the object given as source to current object. Any
       * currently stored buffer is freed or forgotten when
       * approriate.
       *
       * @param[in] aImgData Reference to source to copy from.
       */
      void Copy(const CImageContainer &aImgData);

      /** Write function.
       *
       * This writes image data into file.
       *
       * @param[in] aFilename name for the file.
       * @return    'true' when succesfully written, 'false' on any error.
       */
      bool WriteImageToFile(const std::string &aFilename); 


      /** Returns the current image dataformat.
       * 
       * \see EImageDataType
       *
       * @return Current image dataformat.
       */
      inline EImageDataType GetImageDataType(void) const {
        return (EImageDataType)(imageinfo.imagedatatype);
      }


      /** Return constant reference to ImageInformation structure.
       *
       * \see TImageInfo
       *
       * @return                Constant reference to TImageInfo
       *                        structure of this image.
       */
      inline const TImageInfo &GetImageInfoRef(void) const {
        return imageinfo;
      }
      
      
      /** Return constant pointer to current image data.
       *
       * This function returns pointer to current imagedata. The data
       * is in the format determined by the current value of
       * 'imagedatatype' field of the TImageInfo structure (Which can
       * be inspected by calling the 'GetImageInfoRef()' function) or
       * directly by calling function 'GetImageDataType()'.
       *
       * \see ConvertTo
       * \see GetImageDataType
       *
       * @return Constant pointer to current imagedata.
       */
      inline const unsigned char *GetImageDataPtr(void) const {
        return data;
      }
      

      inline bool GetImageDataPtr2(unsigned char **aData, unsigned int *aSize) const {
        *aData = (unsigned char *)this->data;
        *aSize = this->size;
	return data;
      }

      /** Return size of current image data.
       * 
       * This function returns the number of bytes occupied in the
       * current image format. The data is in the format determined by
       * the current value of 'imagedatatype' field of the TImageInfo
       * structure (Which can be inspected by calling the
       * 'GetImageInfoRef()' function) or directly by calling
       * function 'GetImageDataType()'. 
       *
       * \see ConvertTo
       * \see GetImageDataType
       *
       * @return                Size of imagedata in Bytes.
       */
      inline const unsigned int GetImageDataSize(void) const {
        return size;
      }

    private:
      // Variables

      TImageInfo imageinfo;  ///< Imageinfo for image.
      unsigned char *data;   ///< Pointer to beginning of data.
      unsigned int size;     ///< Size of data in bytes.
      bool isOwnedBuffer;    ///< Flag to determine whether the buffer is owned.

      CImageConversion iConversion;
    };
    
  }
}
#endif
